package controllers

import (
	"fmt"
	"net/http"
	"path"
	"strconv"

	"gitee.com/yanxingshuyuan/backend/server/models"
	"gitee.com/yanxingshuyuan/backend/server/repositories"
	"github.com/gin-gonic/gin"
)

type MatController struct {
	Repo *repositories.MatRepo
}

func NewMatController(r *repositories.MatRepo) *MatController {
	return &MatController{
		Repo: r,
	}
}

func (ctrl *MatController) GetMat(c *gin.Context) {
	mid := c.Param("mid")
	mat := ctrl.Repo.GetMat(mid)
	c.JSON(http.StatusOK, mat)
}

func (ctrl *MatController) GetToc(c *gin.Context) {
	mid := c.Param("mid")
	mat := ctrl.Repo.GetToc(mid)
	c.JSON(http.StatusOK, mat)
}

func (ctrl *MatController) GetResource(c *gin.Context) {
	mid := c.Param("mid")
	mat := ctrl.Repo.GetResource(mid)
	c.JSON(http.StatusOK, mat)
}

func (ctrl *MatController) GetMatByPath(c *gin.Context) {
	path := c.Param("path")
	mat := ctrl.Repo.GetMatByPath(path)
	c.JSON(http.StatusOK, mat)
}

// AddMat 添加新素材
func (ctrl *MatController) AddMat(c *gin.Context) {
	var form models.MatForm
	if err := c.ShouldBindJSON(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if form.Pids == "" {
		user, _ := c.Get("jwtuser")
		form.Pids = user.(*models.User).Pid
	}

	succ := ctrl.Repo.AddMat(&form)

	if succ {
		c.JSON(http.StatusOK, gin.H{"status": "ok", "id": form.ID})
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": "error adding article in repo"})
	}
}

type PathForm struct {
	Path string `json:"path"`
}

// AddMatWithPath 将path对应路径的文件添加到mats素材表中
func (ctrl *MatController) AddMatWithPath(c *gin.Context) {
	path := c.Query("path")
	fmt.Printf("path:%v\n", path)
	mat := ctrl.Repo.AddMatWithPath(path)
	c.JSON(http.StatusOK, mat)
}

func (ctrl *MatController) ModifyMat(c *gin.Context) {
	var mat models.MatForm
	if err := c.ShouldBindJSON(&mat); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// TODO: check if PID from credential is the same as claimed in the post data
	succ := ctrl.Repo.ModifyMat(&mat)

	if succ {
		c.JSON(http.StatusOK, gin.H{"status": "ok", "id": mat.ID})
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": "error adding article in repo"})
	}
}

func (ctrl *MatController) RemoveMat(c *gin.Context) {
	id := c.Param("mid")
	succ := ctrl.Repo.RemoveMat(id)
	if succ {
		c.JSON(http.StatusOK, gin.H{"status": "ok"})
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": "error removing mat in repo"})
	}
}

func (ctrl *MatController) SearchMyArticles(c *gin.Context) {
	user, _ := c.Get("jwtuser")
	mypid := user.(*models.User).Pid
	articles := ctrl.Repo.SearchMats(models.MatSearchForm{Kind: models.KindArticle, Author: mypid})
	c.JSON(http.StatusOK, articles)
}

func (ctrl *MatController) SearchFavProjects(c *gin.Context) {
	user, _ := c.Get("jwtuser")
	mypid := user.(*models.User).Pid
	ctrl.searchMats(c, mypid, models.MatTableMats)
}

func (ctrl *MatController) SearchMyArticleSets(c *gin.Context) {
	user, _ := c.Get("jwtuser")
	mypid := user.(*models.User).Pid
	sets := ctrl.Repo.SearchMatsWithChildren(models.KindColl, mypid)
	c.JSON(http.StatusOK, sets)
}

func (ctrl *MatController) searchMats(c *gin.Context, mypid string, table models.MatTable) {
	kind, err := strconv.Atoi(c.Query("k"))
	if err != nil {
		kind = int(models.KindArticle)
	}
	author := c.Query("author")
	limit, err := strconv.Atoi(c.Query("limit"))
	if err != nil {
		limit = 10
	}
	offset, err := strconv.Atoi(c.Query("offset"))
	if err != nil {
		offset = 0
	}
	orderby := c.Query("o")
	tag := c.Query("tag")
	random := c.Query("r")
	keyword := c.Query("sk")
	searchType := c.Query("st")
	search := models.MatSearchForm{
		Kind:       models.MatKind(kind),
		Mypid:      mypid,
		Author:     author,
		Limit:      uint(limit),
		Offset:     uint(offset),
		Orderby:    orderby,
		Tag:        tag,
		Random:     random == "1",
		Table:      table,
		Keyword:    keyword,
		SearchType: models.SearchType(searchType),
	}
	if mypid != "" {
		search.Mypid = mypid
	}
	res := ctrl.Repo.SearchMats(search)
	c.JSON(http.StatusOK, res)
}

func (ctrl *MatController) SearchMats(c *gin.Context) {
	ctrl.searchMats(c, "", models.MatTableMats)
}

func (ctrl *MatController) SearchResources(c *gin.Context) {
	ctrl.searchMats(c, "", models.MatTableResources)
}

func (ctrl *MatController) MatStats(c *gin.Context) {
	kind, err := strconv.Atoi(c.Query("k"))
	if err != nil {
		kind = int(models.KindArticle)
	}
	stats := ctrl.Repo.MatStats(models.MatKind(kind))
	c.JSON(http.StatusOK, stats)
}

func (ctrl *MatController) SearchFavMats(c *gin.Context) {
	user, _ := c.Get("jwtuser")
	mypid := user.(*models.User).Pid
	ctrl.searchMats(c, mypid, models.MatTableMats)
}

func (ctrl *MatController) SearchPins(c *gin.Context) {
	pid := c.Query("pid")
	news := ctrl.Repo.SearchMats(models.MatSearchForm{Kind: models.KindPin, Author: pid})
	c.JSON(http.StatusOK, news)
}

func (ctrl *MatController) SearchProjects(c *gin.Context) {
	pid := c.Query("pid")
	projects := ctrl.Repo.SearchMats(models.MatSearchForm{Kind: models.KindProject, Author: pid})
	c.JSON(http.StatusOK, projects)
}

func (ctrl *MatController) ImportProject(c *gin.Context) {
	var form models.ProjectImportForm
	if err := c.ShouldBindJSON(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	go ctrl.Repo.ImportProject(&form)
	c.JSON(http.StatusOK, form)
}

func (ctrl *MatController) ListDir(c *gin.Context) {
	source := c.Param("source")
	org := c.Param("org")
	proj := c.Param("proj")
	version := c.Param("version")
	// nodetype := c.Param("nodetype")
	tree := c.Param("tree")
	entries := ctrl.Repo.ListDir(source, path.Join(org, proj), version, tree)
	c.JSON(http.StatusOK, entries)
}

func (ctrl *MatController) GetMatVersions(c *gin.Context) {
	mid := c.Param("mid")
	versions := ctrl.Repo.GetMatVersions(mid)
	c.JSON(http.StatusOK, versions)
}

func (ctrl *MatController) GetProjectVersions(c *gin.Context) {
	source := c.Param("source")
	org := c.Param("org")
	proj := c.Param("proj")
	versions := ctrl.Repo.GetProjectVersions(source, org, proj)
	c.JSON(http.StatusOK, versions)
}
